import json
import os
import time
from datetime import datetime
import logging

import pandas as pd

from django.views.defaults import page_not_found

from backend_django.settings import BASE_DIR, INSTALLED_APPS
# from common.log import Logs
from common.http import render_json, handle_record_operate
from common.token import set_token, parse_payload
from script.generate_app import generate_app
from tools.md5 import salt_passwd, hmac_md5
from user.models import User, Role, Menu, Permission, Requested, Record

# log = Logs(__name__).get_logger()
log = logging.getLogger('my_logger')


# @handle_record_operate(method="login")
def login(request):
    if not request.method == "POST":
        return render_json("Please use POST request!", code=1)
    json_str = request.body
    if not json_str:
        return render_json("Please give me json")
    json_obj = json.loads(json_str)
    user_name = json_obj.get('user_name')
    user_pwd = json_obj.get('user_pwd')

    log.info(f'request login, request user: {user_name}')
    log.info(f'login params: {json_obj}')
    print('2222222222222222222')
    print(json_obj)
    request_url = request.META.get('PATH_INFO')
    request_host = request.META.get('REMOTE_ADDR')
    print(request_url)
    print(request_host)
    try:
        user, created = User.objects.get_or_create(user_name=user_name)
        if created:
            user.role_id = '3'  # only have permission request page
            user.creator = user_name
            key, passwd = salt_passwd(user_pwd)
            user.user_pwd = passwd
            user.key = key
            user.save()
        key = user.key
        user_password = user.user_pwd
        user_passwd = hmac_md5(key, user_pwd)
        if user_password != user_passwd:
            log.info("The username or password is wrong")
            return render_json("The username or password is wrong!", code=1)
        if not user.enable:
            log.info(f'The user is disabled: {user.enable}')
            return render_json("The user is disabled, Please contact the administrator!", code=1)
        payload = {
            'uid': user.id,
            'user_name': user.user_name,
        }
        if user.token_str == '':
            token_str = set_token(payload, timeout=60 * 24 * 30)  # The expired time is one month
            user.token_str = token_str
            user.save()
        else:
            log.info('Use db token to login.')
            token_str = user.token_str
            result = parse_payload(token_str)
            if result.get('error'):
                log.info('The db token is expired, reset token to db.')
                token_str = set_token(payload, timeout=60 * 24 * 30)  # The expired time is one month
                user.token_str = token_str
                user.save()
                return render_json(result.get('error'), code=11)
            uid = result.get('data')['uid']
            if uid:
                try:
                    request.user = User.objects.get(id=uid)
                except:
                    return render_json('用户不存在', code=11)
        data = {'user_name': user.user_name, 'id': user.id, 'token': token_str}
        return render_json(data)
    except Exception as e:
        log.error(f"The user login error: {e}")
        return render_json("login error, please re-login!", code=11)


@handle_record_operate(method="add user")
def add_user(request):
    if not request.method == "POST":
        return render_json("Please use POST request!", code=1)
    json_str = request.body
    if not json_str:
        return render_json("Please give me json")
    json_obj = json.loads(json_str)
    log.info(f'request add_user, request user: {request.user.user_name}')
    log.info(f'add_user params: {json_obj}')
    user_name = json_obj.get('user_name')
    user_pwd = json_obj.pop('user_pwd')
    key, passwd = salt_passwd(user_pwd)

    try:
        res = User.objects.filter(user_name=user_name)
        if res:
            log.info(f"The user is exist: {res[0].to_dict()}")
            return render_json('The user is exist!', code=1)
        User.objects.create(role_id=3, creator=request.user.user_name, **json_obj, key=key, user_pwd=passwd)
        return render_json('create user success!')
    except Exception as e:
        log.error(f"-----create user error is: {e}")
        return render_json('Sorry, the server is busy!', code=1)


@handle_record_operate(method="delete user")
def del_user(request, uid):
    if not request.method == "DELETE":
        return render_json("Please use DELETE request!, The url:/user/del_user/{uid}", code=1)
    log.info(f'request del_user, request user: {request.user.user_name}')
    log.info(f'del_user id: {uid}')
    try:
        if int(uid) > 2:
            User.objects.filter(id=uid).delete()
            return render_json('Delete user successful!')
        else:
            return render_json('Delete user failed, can not delete built in users!')
    except Exception as e:
        log.error(f"-----Delete user error is: {e}")
        return render_json('Sorry,server is busy!', code=1)


@handle_record_operate(method="update user")
def update_user(request, uid):
    if not request.method == "PATCH":
        return render_json(f"Please use PATCH request， The url:/user/update_user/{uid}", code=1)
    json_str = request.body
    if not json_str:
        return render_json("Please give me json")
    json_obj = json.loads(json_str)
    log.info(f'request update_user, request user: {request.user.user_name}')
    log.info(f'update_user params: {json_obj}')
    log.info(f'update_user id: {uid}')
    try:
        if "user_pwd" in json_obj:
            print('update user passwd')
            user_pwd = json_obj.pop('user_pwd')
            key, passwd = salt_passwd(user_pwd)
            User.objects.filter(id=uid).update(**json_obj, update_at=datetime.now(), updater=request.user.user_name,
                                               key=key, user_pwd=passwd)
        else:
            User.objects.filter(id=uid).update(**json_obj, update_at=datetime.now(), updater=request.user.user_name)
        return render_json('update success!')
    except Exception as e:
        log.error(f"-----update error is: {e}")
        return render_json('Sorry,server is busy!', code=1)


@handle_record_operate(method="get user info")
def get_user_info(request, uid):
    log.info(f'request get_user_info, request user: {request.user.user_name}')
    log.info(f'get_user_info id: {uid}')
    try:
        res = User.objects.get(id=uid)
        return render_json(res.to_dict())
    except Exception as e:
        log.error(f"-----get_user_info error is: {e}")
        return render_json('Sorry,server is busy!', code=1)


@handle_record_operate(method="get user list")
def get_user_list(request):
    if not request.method == "POST":
        return render_json("Please use POST request!", code=1)
    json_str = request.body
    if not json_str:
        return render_json("Please give me json")
    json_obj = json.loads(json_str)
    # print('www', json_obj)
    log.info(f'request get_user_list, request user: {request.user.user_name}')
    log.info(f'get_user_list params: {json_obj}')
    offset = json_obj['offset']
    limit = json_obj['size'] + offset
    try:
        filter_args = {}
        if 'filter_field' in json_obj:
            for key, value in json_obj['filter_field'].items():
                if value != '':
                    if key == "create_at" or key == 'updated_at':
                        if len(value) == 2:
                            key = key + "__range"
                    elif key == 'enable':
                        pass
                    else:
                        key = key + "__icontains"
                    filter_args[key] = value
        log.info(f'filter_args: {filter_args}')
        if filter_args:
            res = User.objects.filter(**filter_args).order_by('-id')
        else:
            res = User.objects.all().order_by('-id')
        data_list = [i.to_dict() for i in res[offset:limit]]
        # print("res: ", data_list)
        count = res.count()
        data = {"totalCount": count, "list": data_list}
        return render_json(data=data)
    except Exception as e:
        log.error(f"-----Select user error is: {e}")
        return render_json('Sorry,server is busy!', code=1)


def export_user_list(request):
    if not request.method == "POST":
        return render_json("请使用POST请求", code=1)

    json_str = request.body
    if not json_str:
        return render_json("Please give me json")

    json_obj = json.loads(json_str)
    log.info(f'request export_user_list, request user: {request.user.user_name}')
    log.info(f'export_user_list params: {json_obj}')
    filter_args = {}
    if 'filter_field' in json_obj:
        for key, value in json_obj['filter_field'].items():
            if value != '':
                if key == "create_at" or key == 'updated_at':
                    if len(value) == 2:
                        key = key + "__range"
                elif key == 'enable':
                    pass
                else:
                    key = key + "__icontains"
                filter_args[key] = value
    log.info(f'filter_args: {filter_args}')
    try:
        if filter_args:
            res = User.objects.filter(**filter_args).order_by('-id')
        else:
            res = User.objects.all().order_by('-id')
        data_list = [i.to_dict() for i in res]
        df = pd.DataFrame(data_list)
        wt = str(int(time.time()))
        file_path = os.path.join(BASE_DIR, 'static', 'output', 'user')
        if not os.path.exists(file_path):
            os.makedirs(file_path)
        filepath = os.path.join(file_path, "export-user_list-" + wt + ".xlsx")
        log.info(f"export file path: {filepath}")
        df.to_excel(filepath, index=False)
        return render_json({'filepath': filepath.replace(str(BASE_DIR), "")})
    except Exception as e:
        log.error(f"-----Select download record list error is: {e}")
        return render_json('Sorry,server is busy!', code=1)


# role api ####################################################
@handle_record_operate(method="add role")
def add_role(request):
    if not request.method == "POST":
        return render_json("Please use POST request", code=1)
    json_str = request.body
    if not json_str:
        return render_json("Please give me json")
    json_obj = json.loads(json_str)
    log.info(f'request add_role, request user: {request.user.user_name}')
    log.info(f'add_role params: {json_obj}')
    try:
        Role.objects.create(**json_obj, creator=request.user.user_name)
        Permission.objects.create(name=json_obj['name'], creator=request.user.user_name)
        return render_json('Create Role successful!')
    except Exception as e:
        log.error(f"-----Create role error is: {e}")
        return render_json('Sorry, Server is busy!')


@handle_record_operate(method="delete role")
def del_role(request, rid):
    if not request.method == "DELETE":
        return render_json("Please use DELETE request!", code=1)
    log.info(f'request del_role, request user: {request.user.user_name}')
    log.info(f'del_role id: {rid}')
    try:
        if int(rid) > 2:
            Role.objects.filter(id=rid).delete()
            return render_json('Delete role successful!')
        else:
            return render_json('Delete role failed, can not delete built in role!')
    except Exception as e:
        log.error(f"-----Delete role error: {e}")
        return render_json('Sorry,server is busy!', code=1)


@handle_record_operate(method="update role")
def update_role(request, rid):
    if not request.method == "PATCH":
        return render_json(f"Please use PATCH request, The url:user/update_role/{rid}", code=1)
    json_str = request.body
    if not json_str:
        return render_json("Please give me json")
    json_obj = json.loads(json_str)
    log.info(f'request update_role, request user: {request.user.user_name}')
    log.info(f'update_role params: {json_obj}')
    log.info(f'update_role id: {rid}')
    try:
        Role.objects.filter(id=rid).update(**json_obj, update_at=datetime.now(), updater=request.user.user_name)
        return render_json('Update success!')
    except Exception as e:
        log.error(f"-----update_role error is: {e}")
        return render_json('Sorry,server is busy!', code=1)


@handle_record_operate(method="get role list")
def get_role_list(request):
    if not request.method == "POST":
        return render_json("Please use POST request!", code=1)
    json_str = request.body
    if not json_str:
        return render_json("Please give me json")
    json_obj = json.loads(json_str)
    log.info(f'request get_role_list, request user: {request.user.user_name}')
    log.info(f'get_role_list params: {json_obj}')
    print('get_role_list', json_obj)
    offset = json_obj['offset']
    limit = json_obj['size'] + offset
    try:
        filter_args = {}
        if 'filter_field' in json_obj:
            for key, value in json_obj['filter_field'].items():
                if value != '':
                    if key == "create_at" or key == 'updated_at':
                        if len(value) == 2:
                            key = key + "__range"
                    else:
                        key = key + "__icontains"
                    filter_args[key] = value
        log.info(f'filter_args: {filter_args}')
        print('filter_args', filter_args)
        if filter_args:
            res = Role.objects.filter(**filter_args)
        else:
            res = Role.objects.all()
        data_list = [i.to_dict() for i in res[offset:limit]]
        count = res.count()
        data = {"totalCount": count, "list": data_list}
        return render_json(data)
    except Exception as e:
        log.error(f"-----Select role error is: {e}")
        return render_json('Sorry,server is busy!', code=1)


# menu api ####################################################
@handle_record_operate(method="add menu")
def add_menu(request):
    if not request.method == "POST":
        return render_json("Please use POST request", code=1)
    json_str = request.body
    if not json_str:
        return render_json("Please give me json")
    json_obj = json.loads(json_str)
    log.info(f'request add_menu, request user: {request.user.user_name}')
    log.info(f'add_menu params: {json_obj}')
    try:
        Menu.objects.create(**json_obj, creator=request.user.user_name)
        return render_json('Create menu successfully!')
    except Exception as e:
        log.error(f"-----Create error is: {e}")
        return render_json('Sorry, Server is busy!')


@handle_record_operate(method="del menu")
def del_menu(request, mid):
    if not request.method == "DELETE":
        return render_json("Please use DELETE request", code=1)
    try:
        log.info(f'request del_menu, request user: {request.user.user_name}')
        log.info(f'del_menu id: {mid}')
        Menu.objects.filter(id=mid).delete()
        return render_json('Delete menu successful!')
    except Exception as e:
        log.error(f"-----Delete menu error: {e}")
        return render_json('Sorry,server is busy!', code=1)


@handle_record_operate(method="update menu")
def update_menu(request, mid):
    if not request.method == "PATCH":
        return render_json("Please use PATCH request!", code=1)
    json_str = request.body
    if not json_str:
        return render_json("Please give me json")
    json_obj = json.loads(json_str)
    log.info(f'request update_menu, request user: {request.user.user_name}')
    log.info(f'update_menu params: {json_obj}')
    try:
        Menu.objects.filter(id=mid).update(**json_obj, update_at=datetime.now(), updater=request.user.user_name)
        return render_json('Update menu successfully!')
    except Exception as e:
        log.error(f"-----Update menu error: {e}")
        return render_json('Sorry, server is busy!', code=1)


@handle_record_operate(method="get menu list")
def get_menu_list(request):
    if not request.method == "POST":
        return render_json("Please use GET request!", code=1)
    try:
        res = Menu.objects.all()
        res = [i.to_dict() for i in res]
        tree = Menu.make_menu_tree(res)
        data = {"list": tree}
        return render_json(data)
    except Exception as e:
        log.error(f"-----Get menu list error: {e}")
        return render_json('Sorry, server is busy!', code=1)


@handle_record_operate(method="get user menu by role id")
def get_user_menu_by_role_id(request):
    if not request.method == "POST":
        return render_json("Please use POST request", code=1)
    json_str = request.body
    if not json_str:
        return render_json("Please give me json")
    json_obj = json.loads(json_str)
    log.info(f'request get_user_menu_by_role_id, request user: {request.user.user_name}')
    log.info(f'get_user_menu_by_role_id params: {json_obj}')
    print('get_user_menu_by_role_id:', json_obj)
    role_id_list = json_obj['role_id'].split(',')
    role_id_list = list(set(role_id_list))
    log.info(f"role id list: {role_id_list}")
    try:
        menu_list = []
        for i in role_id_list:
            rid = int(i)
            menuList = Role.objects.get(id=rid).menu_list
            menu_list += eval(menuList)
        menu_list = list(set(menu_list))
        role_menu_list = Menu.make_menu_tree([i.to_dict() for i in Menu.objects.filter(pk__in=menu_list)])
        res = {'menu_list': role_menu_list}
        return render_json(res)
    except Exception as e:
        log.error(f"-----Get user menu by role id error: {e}")
        return render_json('Sorry, server is busy!', code=1)


# permission API ######################
def get_permissions(request):
    if not request.method == "POST":
        return render_json("Please use POST request", code=1)
    log.info(f'request get_permissions, request user: {request.user.user_name}')
    try:
        res = Permission.objects.all()
        requests = Requested.objects.filter(user_id=request.user.id)
        requests_res = {}
        for i in requests:
            request_obj = i.to_dict()
            request_list = []
            request_list.append(request_obj)
            if request_list:
                key = request_obj['permission_name']
                requests_res[key] = request_list
        log.debug(f"request res: {requests_res}")
        permission_list = []
        for i in res:
            item = i.to_dict()
            if item['name'] in requests_res.keys():
                item['requested'] = True
            else:
                item['requested'] = False
            permission_list.append(item)

        data = {"request_obj": requests_res, "permission_list": permission_list}
        log.info(f"permission result: {data}")
        return render_json(data)
    except Exception as e:
        log.error(f"-----Get permission error is: {e}")
        return render_json('Sorry,server is busy!', code=1)


def add_permission(request):
    if not request.method == "POST":
        return render_json("Please use POST request", code=1)
    json_str = request.body
    if not json_str:
        return render_json("Please give me json")
    json_obj = json.loads(json_str)
    log.info(f'request add_permission, request user: {request.user.user_name}')
    log.info(f'add_permission params: {json_obj}')
    try:
        Permission.objects.create(**json_obj)
        return render_json('Create permission success!')
    except Exception as e:
        log.error(f"-----Create permission error: {e}")
        return render_json('Sorry, Server is busy!')


def request_permission(request):
    if not request.method == "POST":
        return render_json("Please use POST request!", code=1)
    json_str = request.body
    if not json_str:
        return render_json("Please give me json")
    json_obj = json.loads(json_str)
    log.info(f'request request_permission, request user: {request.user.user_name}')
    log.info(f'request_permission params: {json_obj}')
    try:
        Requested.objects.create(user_id=request.user.id, permission_id=json_obj['permission']['id'])
        return render_json('Create Requested permission successful!')
    except Exception as e:
        log.error(f"-----Create request permission error: {e}")
        return render_json('Sorry, Server is busy!')


def get_requests(request):
    if not request.method == "POST":
        return render_json("Please use POST request!", code=1)
    json_str = request.body
    if not json_str:
        return render_json("Please give me json")
    json_obj = json.loads(json_str)
    log.info(f'request get_requests, request user: {request.user.user_name}')
    log.info(f'get_requests params: {json_obj}')
    print("get request list params:", json_obj)
    offset = json_obj['offset']
    limit = json_obj['size'] + offset
    try:
        filter_args = {}
        if 'filter_field' in json_obj:
            for key, value in json_obj['filter_field'].items():
                if value != '':
                    if "user_name" == key:
                        filter_args['user_id'] = value
                    elif "permission_name" == key:
                        filter_args['permission_id'] = value
                    else:
                        filter_args[key] = value
        log.info(f"filter_args: {filter_args}")
        print('filter args: ', filter_args)
        if filter_args:
            res = Requested.objects.filter(**filter_args).order_by('-id')
        else:
            res = Requested.objects.all().order_by('-id')
        count = res.count()
        data_list = [i.to_dict() for i in res[offset:limit]]
        data = {"totalCount": count, "list": data_list}
        return render_json(data=data)
    except Exception as e:
        log.error(f"-----Get requests error : {e}")
        return render_json('Sorry, server is busy!', code=1)


def approve_request(request):
    if not request.method == "POST":
        return render_json("Please use POST request", code=1)
    json_str = request.body
    if not json_str:
        return render_json("Please give me json", code=1)
    json_obj = json.loads(json_str)
    log.info(f'request approve_request, request user: {request.user.user_name}')
    log.info(f'approve_request params: {json_obj}')
    user_name = json_obj['user_name']
    permission_name = json_obj['permission_name']
    try:
        role_id = Role.objects.get(name=permission_name).id
        user = User.objects.get(user_name=user_name)
        user_id = user.id
        per_id = Permission.objects.get(name=permission_name).id
        Requested.objects.filter(user_id=user_id, permission_id=per_id).update(status=2, approver=request.user.user_name)
        user.roleId = user.role_id + ',' + str(role_id)
        user.save()
        return render_json('Update success!')
    except Exception as e:
        log.error(f"-----Create error is: {e}")
        return render_json('Sorry,server is busy!', code=1)


def filter_user_name(request):
    if not request.method == "POST":
        return render_json("请使用POST请求", code=1)

    json_str = request.body
    if not json_str:
        return render_json("Please give me json")

    json_obj = json.loads(json_str)
    # print(json_obj)
    log.info(f'request filter_user_name, request user: {request.user.user_name}')
    log.info(f'filter_user_name params: {json_obj}')
    query = json_obj['query']
    try:
        res = User.objects.filter(user_name__icontains=query)
        l = [i.to_user_name() for i in res]
        count = res.count()
        log.debug(f"filter user name list: {l}")
        data = {"totalCount": count, "list": l}
        return render_json(data=data)
    except Exception as e:
        log.error(f"-----Select user name error is: {e}")
        return render_json('Sorry,server is busy!', code=1)


def filter_permission_name(request):
    if not request.method == "POST":
        return render_json("请使用POST请求", code=1)

    json_str = request.body
    if not json_str:
        return render_json("Please give me json")

    json_obj = json.loads(json_str)
    # print(json_obj)
    log.info(f'request filter_permission_name, request user: {request.user.user_name}')
    log.info(f'filter_permission_name params: {json_obj}')
    query = json_obj['query']
    try:
        res = Permission.objects.filter(name__icontains=query)
        l = [i.to_permission_name() for i in res]
        count = res.count()
        log.debug(f"filter permission name list: {l}")
        data = {"totalCount": count, "list": l}
        return render_json(data=data)
    except Exception as e:
        log.error(f"-----Select permission name error is: {e}")
        return render_json('Sorry,server is busy!', code=1)


def download_request_list(request):
    if not request.method == "GET":
        return render_json("Please use GET request method!", code=1)
    log.info(f'request download_request_list, request user: {request.user.user_name}')
    try:
        res = Requested.objects.all().order_by('-id')
        l = [i.to_export_dict() for i in res]

        df = pd.DataFrame(l)
        # print(df)
        wt = str(int(time.time()))
        file_path = os.path.join(BASE_DIR, 'static', 'output', 'request')
        if not os.path.exists(file_path):
            os.makedirs(file_path)
        filepath = os.path.join(file_path, "export-request_list-" + wt + ".xlsx")
        log.info(f"export file path: {filepath}")
        df.to_excel(filepath, index=False)
        return render_json({'filepath': filepath.replace(str(BASE_DIR), "")})
    except Exception as e:
        log.error(f"-----Select Product list error is: {e}")
        return render_json('Sorry,server is busy!', code=1)


# record api ###########################################################
def get_record_list(request):
    if not request.method == "POST":
        return render_json("Please use POST request!", code=1)
    json_str = request.body
    if not json_str:
        return render_json("Please give me json")
    json_obj = json.loads(json_str)
    log.info(f'request get_records, request user: {request.user.user_name}')
    log.info(f'get_records params: {json_obj}')
    print('get_records:', json_obj)
    offset = json_obj['offset']
    limit = json_obj['size'] + offset
    try:
        filter_args = {}
        if 'filter_field' in json_obj:
            for key, value in json_obj['filter_field'].items():
                if value != '':
                    if "range" in key:
                        if len(value) == 2:
                            key = "request_at__range"
                    else:
                        key = key + "__icontains"
                    filter_args[key] = value
        log.info(f"filter_args: {filter_args}")
        print('filter args', filter_args)
        if filter_args:
            res = Record.objects.filter(**filter_args).order_by('-id')
        else:
            res = Record.objects.all().order_by('-id')
        count = res.count()
        data_list = [i.to_dict() for i in res[offset:limit]]
        data = {"totalCount": count, "list": data_list}
        return render_json(data=data)
    except Exception as e:
        log.error(f"-----Get record error : {e}")
        return render_json('Sorry, server is busy!', code=1)


def custom_page_not_found(request, exception):
    return page_not_found(request, exception)


def get_table_sync(request):
    json_str = request.body
    json_obj = json.loads(json_str)
    log.info(f'request get_table_sync, request user: {request.user.user_name}')
    log.info(f'get_table_sync params: {json_obj}')
    log.debug(f'get_table_sync: {json_obj}')
    table_name = json_obj['table_name']
    app_name = json_obj['app_name']
    if app_name in INSTALLED_APPS:
        is_exist_app = True
    else:
        is_exist_app = False
        generate_app(app_name)

    from django.db import connections
    cursor = connections['default'].cursor()
    sqls = f"""
        select COLUMN_NAME as field 
        FROM INFORMATION_SCHEMA.COLUMNS
        WHERE TABLE_SCHEMA = 'backend_django' and TABLE_NAME = '{table_name}';
    """
    cursor.execute(sqls)
    desc = cursor.description
    result = [
        dict(zip([col[0] for col in desc], row))
        for row in cursor.fetchall()
    ]
    cursor.close()
    if result:
        print('table field result: ', result)
        code = 1
        field_tree_data = handle_el_tree_data('field', result)
        data = {"list": field_tree_data, 'code': code, 'is_exist_app': is_exist_app}
    else:
        code = 0
        data = {"list": [], 'code': code, 'is_exist_app': is_exist_app}
    log.debug(f'result: {data}')
    return render_json(data=data)


def handle_el_tree_data(name, product_filter):
    tree_product = [
        {
            "children": []
        }
    ]
    tree_product[0][name] = 'All'
    tree_product[0]['children'] = product_filter
    return tree_product
